package arkham.data;

import java.awt.Color;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import arkham.data.Globals.EffectUsage;
import arkham.data.Globals.UsablePhase;
import arkham.jaxb.encounter.*;
import arkham.util.AHLogger;
import arkham.util.ImageUtils;
import arkham.util.XmlTools;

public class PersonalEffect implements EffectSource
{
	private String name;
	private String description;
	private boolean unique;
	private boolean firstTurn;
	private int numCounters, limit;
	private boolean exhausted;
	private BufferedImage img;
	private Color preferredColor;
	private HashMap<String, String> properties;
	private List<ModEffect> modEffects;
	private List<UsableEffect> usableEffects;

	public PersonalEffect(String name)
	{
		this.name = name;
		modEffects = new ArrayList<ModEffect>();
		usableEffects = new ArrayList<UsableEffect>();
		properties = new HashMap<String, String>();
		preferredColor = Color.white;
		firstTurn = true;
	}

	/*
	 * (non-Javadoc)
	 * @see arkham.data.NamedEntity#getName()
	 */
	public String getName() {
		return name;
	}

	/**
	 * Gets the description of this effect
	 * @return the description
	 */
	public String getDescription() {
		return description;
	}

	/**
	 * Checks if this effect is unique, ie. each investigator can have at most
	 * one.
	 * @return true if an investigator can only have one copy of this effect
	 */
	public boolean isUnique() {
		return unique;
	}

	/**
	 * Gets the preferred color for this effect's background when displayed
	 * on the effects pane
	 * @return the preferred background color
	 */
	public Color getPreferredColor()
	{
		return preferredColor;
	}

	/**
	 * Gets the image associated with this effect
	 * @return the image
	 */
	public BufferedImage getImage() {
		return img;
	}

	/**
	 * Sets the icon image of this personal effect
	 * @param filename the filename of the image
	 */
	public void setImage(String filename)
	{
		if (filename != null)
		{
			img = ImageUtils.loadImage(filename);
			img = ImageUtils.resizeImage(img, 25, 25);
		}
	}

	@Override
	public List<ModEffect> getModEffects() {
		return modEffects;
	}

	/**
	 * Checks if this effect is still in its first turn, in other words,
	 * has just been applied and no roll will be made to discard it this turn.
	 * @return true if this effect is still in its first turn
	 */
	public boolean isFirstTurn()
	{
		return firstTurn;
	}

	/**
	 * Set this to show that the effect is no longer in its first turn
	 */
	public void advanceTurn()
	{
		firstTurn = false;
	}

	/**
     * Checks if a property is defined for this investigator card
     * @param property The property to check
     * @return true if it is defined, false otherwise
     */
    public boolean hasProperty(String property)
    {
        return properties.containsKey(property);
    }

    /**
     * Gets a property of this investigator card
     * @param property The name of the property
     * @return The retrieved property
     */
    public String getProperty(String property)
    {
        return properties.get(property);
    }

	public PersonalEffect clone()
	{
		PersonalEffect peff = new PersonalEffect(name);
		peff.description = description;
		peff.img = img;
		peff.modEffects.addAll(modEffects);
		peff.usableEffects.addAll(usableEffects);
		peff.preferredColor = preferredColor;
		peff.unique = unique;
		return peff;
	}

	@Override
	public List<ModEffect> searchModEffects(String type)
	{
		List<ModEffect> list = new ArrayList<ModEffect>();
		for (ModEffect mod : modEffects)
		{
			if (mod.getType().equalsIgnoreCase(type))
			{
				list.add(mod);
			}
		}
		return list;
	}

	@Override
	public void searchModEffects(List<ModEffect> list, String type)
	{
		for (ModEffect mod : modEffects)
		{
			if (mod.getType().equalsIgnoreCase(type))
			{
				list.add(mod);
			}
		}
	}

	@Override
	public List<ModEffect> searchModEffects(String type, String details)
	{
		List<ModEffect> list = new ArrayList<ModEffect>();
		for (ModEffect mod : modEffects)
		{
			if (mod.getType().equalsIgnoreCase(type) &&
					mod.getDetails().equalsIgnoreCase(details))
			{
				list.add(mod);
			}
		}
		return list;
	}

	@Override
	public void searchModEffects(List<ModEffect> list, String type,
			String details)
	{
		for (ModEffect mod : modEffects)
		{
			if (mod.getType().equalsIgnoreCase(type) &&
					mod.getDetails().equalsIgnoreCase(details))
			{
				list.add(mod);
			}
		}
	}

	@Override
	public void setExhausted(boolean exhausted)
	{
		this.exhausted = exhausted;
	}

	@Override
	public boolean isExhausted()
	{
		return exhausted;
	}

	@Override
	public void setNumCounters(int numCounters)
	{
		this.numCounters = numCounters;
	}

	@Override
	public int getNumCounters()
	{
		return numCounters;
	}

    @Override
    public int getLimit()
    {
        return limit;
    }

    @Override
    public void setLimit(int limit)
    {
        this.limit = limit;
    }

	/*
     * (non-Javadoc)
     * @see arkham.data.EffectSource#getUsableEffects()
     */
    public List<UsableEffect> getUsableEffects() {
        return usableEffects;
    }

    /*
     * (non-Javadoc)
     * @see arkham.data.EffectSource#searchUsableEffects(arkham.data.Globals.UsablePhase)
     */
    public List<UsableEffect> searchUsableEffects(UsablePhase phase)
    {
        List<UsableEffect> list = new ArrayList<UsableEffect>();
        for (UsableEffect ueff : usableEffects)
        {
            if (ueff.getPhase().equals(phase) && !ueff.getUsage().equals(EffectUsage.ALWAYS))
            {
                list.add(ueff);
            }
        }
        return list;
    }

    @Override
    public void searchUsableEffects(List<UsableEffect> list,
            UsablePhase phase)
    {
        for (UsableEffect ueff : usableEffects)
        {
            if (ueff.getPhase().equals(phase) && !ueff.getUsage().equals(EffectUsage.ALWAYS))
            {
                list.add(ueff);
            }
        }
    }

    @Override
    public List<UsableEffect> searchAutoUsableEffects(UsablePhase phase)
    {
        List<UsableEffect> list = new ArrayList<UsableEffect>();
        for (UsableEffect ueff : usableEffects)
        {
            if (ueff.getPhase().equals(phase) && ueff.getUsage().equals(EffectUsage.ALWAYS))
            {
                list.add(ueff);
            }
        }
        return list;
    }

    @Override
    public void searchAutoUsableEffects(List<UsableEffect> list,
            UsablePhase phase)
    {
        for (UsableEffect ueff : usableEffects)
        {
            if (ueff.getPhase().equals(phase) && ueff.getUsage().equals(EffectUsage.ALWAYS))
            {
                list.add(ueff);
            }
        }
    }

    @Override
    public List<ModEffect> searchAlwaysModEffects(String type)
    {
        List<ModEffect> list = new ArrayList<ModEffect>();
        for (ModEffect mod : modEffects)
        {
            if (mod.getType().equalsIgnoreCase(type) &&
                    mod.getUsage().equals(EffectUsage.ALWAYS))
            {
                list.add(mod);
            }
        }
        return list;
    }

    @Override
    public List<ModEffect> searchAlwaysModEffects(String type, String details)
    {
        List<ModEffect> list = new ArrayList<ModEffect>();
        for (ModEffect mod : modEffects)
        {
            if (mod.getType().equalsIgnoreCase(type) &&
                    mod.getDetails().equalsIgnoreCase(details) &&
                    mod.getUsage().equals(EffectUsage.ALWAYS))
            {
                list.add(mod);
            }
        }
        return list;
    }

    @Override
    public String getIdentifier()
    {
        return "%" + name;
    }

	@Override
	public String toString()
	{
	    return name;
	}

	@Override
	public boolean equals(Object o)
	{
	    if (!(o instanceof PersonalEffect))
	    {
	        return false;
	    }
	    PersonalEffect peff = (PersonalEffect)o;
	    return name.equals(peff.getName()) && numCounters == peff.getNumCounters()
	        && exhausted == peff.isExhausted() && firstTurn == peff.isFirstTurn();
	}

	/**
	 * This method creates a data object from the XML representation of the
	 * object. It does so by invoking the appropriate constructor of the
	 * appropriate child class based on the specified type.
	 *
	 * @param xmlcard The XML based card info
	 * @return The data representation of the personal effect
	 */
	public static PersonalEffect createEffectFromXml(PersonalEffectType xmlpeff)
	{
		PersonalEffect peff = new PersonalEffect(xmlpeff.getName());
		peff.description = XmlTools.unescapeString(xmlpeff.getDescription());
		peff.setImage(xmlpeff.getImg());
		peff.unique = xmlpeff.isUnique();
		for (PropertyType prop : xmlpeff.getProperty())
        {
            peff.properties.put(prop.getName(), prop.getValue());
        }
		if (XmlTools.isPropertyDefined(xmlpeff, "Limit"))
        {
            try
            {
                peff.limit = XmlTools.getIntPropertyValue(xmlpeff, "Limit");
                if (peff.limit <= 0)
                {
                    throw new NumberFormatException();
                }
            }
            catch (NumberFormatException e)
            {
                AHLogger.error(xmlpeff.getName() + " has specified an invalid limit: " +
                        XmlTools.getStrPropertyValue(xmlpeff, "Limit"));
                return null;
            }
        }
		for (ModEffectType xmleffect : xmlpeff.getModEffect())
		{
			ModEffect mod = ModEffect.createModEffectFromXml(xmleffect);
			if (mod != null)
			{
			    mod.setSource(peff);
				peff.getModEffects().add(mod);
			}
			else
			{
				return null;
			}
		}
		for (UsableEffectType xmleffect : xmlpeff.getUsableEffect())
        {
            UsableEffect ueff = UsableEffect.createModEffectFromXml(xmleffect);
            if (ueff == null)
            {
                return null;
            }
            ueff.setSource(peff);
            peff.getUsableEffects().add(ueff);
        }
		if (xmlpeff.getColor() != null)
		{
			Color col = XmlTools.translateColorString(xmlpeff.getColor());
			peff.preferredColor = col;
		}
		return peff;
	}
}
